//-------------------------------------------------------------------
//	Copyright (c) 2010-2012 Zhirnov Andrey
//	This file is part of the "UXGEN" project.
//	See copyright notice in "Engine.h".
//-------------------------------------------------------------------

#include "CameraComp.h"

namespace Engine
{
namespace Scene
{
	
	const string	CCameraComp::_s_sCameraCompTypeName( "CameraComponent" );



//------------------------- CCameraComp ---------------------------//
/*
=================================================
	
=================================================
*/
	CCameraComp::CCameraComp():
		_eCamType(e_camera::VIEW), _uMaxLOD(0)
	{
	}
	
/*
=================================================
	
=================================================
*/
	CCameraComp::~CCameraComp()
	{
	}
	
/*
=================================================
	
=================================================
*/
	void CCameraComp::Free()
	{
		delete this;
	}
		
/*
=================================================
	
=================================================
*/
	bool CCameraComp::ProcessMessage(TSNCompMessage &)
	{
		return false;
	}
	
/*
=================================================
	
=================================================
*/
	bool CCameraComp::GetMetaIterator(CMetaSetIterator *&)
	{
		return false;
	}
	
/*
=================================================
	
=================================================
*/
	e_component::type CCameraComp::GetComponentBits() const
	{
		return e_component::CAMERA;
	}
	
/*
=================================================
	
=================================================
*/
	const string & CCameraComp::GetTypeName() const
	{
		return _s_sCameraCompTypeName;
	}

/*
=================================================
	
=================================================
*/
	bool CCameraComp::Bind()
	{
		MSTACK()->SetMatrixMVP( mat4::Identity(), _sCamera.ViewMatrix(),
								_sCamera.ProjMatrix(), _sCamera.ViewProjMatrix() );
		return true;
	}
	
/*
=================================================
	
=================================================
*/
	bool CCameraComp::Unbind()
	{
		return true;
	}

/*
=================================================
	
=================================================
*/
	void CCameraComp::GetParameters(const mat4 **pView, const mat4 **pProj,
									const frustum_t **pFrustum,
									vec3 *pRotation, vec3 *pPosition,
									vec2 *pClipPlanes, uint8 *pMaxLOD)
	{
		if ( pView != nullptr )			*pView			= &_sCamera.ViewMatrix();
		if ( pProj != nullptr )			*pProj			= &_sCamera.ProjMatrix();
		if ( pFrustum != nullptr )		*pFrustum		= &_sCamera.Frustum();
		if ( pRotation != nullptr )		*pRotation		= _sCamera.Rotation();
		if ( pPosition != nullptr )		*pPosition		= _sCamera.Position();
		if ( pClipPlanes != nullptr )	*pClipPlanes	= _sCamera.ClipPlanes();
		if ( pMaxLOD != nullptr )		*pMaxLOD		= _uMaxLOD;
	}
	
/*
=================================================
	
=================================================
*/
	CViewport * CCameraComp::GetViewport()
	{
		return (CViewport *)this;
	}
	
/*
=================================================
	
=================================================
*/
	e_camera::type CCameraComp::GetCameraType() const
	{
		return _eCamType;
	}
	
/*
=================================================
	
=================================================
*/
	void CCameraComp::Release()
	{}



//--------------------- CInputCameraComp --------------------------//
/*
=================================================
	
=================================================
*/
	CInputCameraComp::CInputCameraComp():
		_vAxisSens(1.f),	_fVelocity(500.f),	_fMouseSens(0.07f)
	{
		_pUpdate = & CInputCameraComp::_UpdateFPS;

		_aActions.PushBack( TKeyAction( & CInputCameraComp::_MoveForward,  1, CA_MOVE_FORWARD,  e_key::W ) );
		_aActions.PushBack( TKeyAction( & CInputCameraComp::_MoveForward, -1, CA_MOVE_BACKWARD, e_key::S ) );
		
		_aActions.PushBack( TKeyAction( & CInputCameraComp::_MoveLeft, -1, CA_MOVE_LEFT,  e_key::A ) );
		_aActions.PushBack( TKeyAction( & CInputCameraComp::_MoveLeft,  1, CA_MOVE_RIGHT, e_key::D ) );
		
		_aActions.PushBack( TKeyAction( & CInputCameraComp::_MoveUp, -1, CA_MOVE_UP,   e_key::SPACE ) );
		_aActions.PushBack( TKeyAction( & CInputCameraComp::_MoveUp,  1, CA_MOVE_DOWN, e_key::LSHIFT ) );
		
		_aActions.PushBack( TKeyAction( & CInputCameraComp::_Increase,  1, CA_INCREASE, e_key::MWHEEL_UP ) );
		_aActions.PushBack( TKeyAction( & CInputCameraComp::_Increase, -1, CA_DECREASE, e_key::MWHEEL_DOWN ) );
		
		_aActions.PushBack( TKeyAction( & CInputCameraComp::_Roll,  1, CA_ROLL_POS, e_key::Q ) );
		_aActions.PushBack( TKeyAction( & CInputCameraComp::_Roll, -1, CA_ROLL_NEG, e_key::E ) );
		
		_aActions.PushBack( TKeyAction( & CInputCameraComp::_Yaw,  1, CA_YAW_POS, e_key::UP ) );
		_aActions.PushBack( TKeyAction( & CInputCameraComp::_Yaw, -1, CA_YAW_NEG, e_key::DOWN ) );
		
		_aActions.PushBack( TKeyAction( & CInputCameraComp::_Pitch,  1, CA_PITCH_POS, e_key::LEFT ) );
		_aActions.PushBack( TKeyAction( & CInputCameraComp::_Pitch, -1, CA_PITCH_NEG, e_key::RIGHT ) );
	}
	
/*
=================================================
	
=================================================
*/
	CInputCameraComp::~CInputCameraComp()
	{
	}
		
/*
=================================================
	
=================================================
*/
	void CInputCameraComp::Create(const vec3 &vPos, const vec3 &vRot, const vec2 &vClip,
								  const irect &sView, float fFovY, float fAspect)
	{
		_fDist	= 0.f;
		_sRect	= sView;
		_sCamera.Create( transform_t( vPos, quat( vRot ) ), fFovY, fAspect, vClip );
	}

/*
=================================================
	
=================================================
*/
	void CInputCameraComp::OnResize(const irect &sView, float fFovY, float fAspect)
	{
		_sRect	= sView;
		_sCamera.Resize( fFovY, fAspect );
	}

/*
=================================================
	
=================================================
*/
	void CInputCameraComp::Create(const vec3 &vPos, const vec3 &vRot, const vec2 &vClip,
								  const irect &sView, float fDist)
	{
		_fDist	= fDist;
		_sRect	= sView;
		_sCamera.Create( transform_t( vPos, quat( vRot ) ), OSAPI()->GetRealViewportSize( Size() ), fDist, vClip );
	}

/*
=================================================
	
=================================================
*/
	void CInputCameraComp::OnResize(const irect &sView)
	{
		_sRect	= sView;
		_sCamera.Resize( OSAPI()->GetRealViewportSize( Size() ), _fDist );
	}
	
/*
=================================================
	
=================================================
*/
	void CInputCameraComp::Create(const vec3 &vPos, const vec3 &vRot, const vec2 &vClip, float fDist)
	{
		_fDist	= fDist;
		_sRect	= irect( ivec2(0), WINDOW()->GetRenderParams().sWinSize );
		_sCamera.Create( transform_t( vPos, quat( vRot ) ), OSAPI()->GetRealViewportSize( Size() ), fDist, vClip );
	}

/*
=================================================
	
=================================================
*/
	void CInputCameraComp::OnResize()
	{
		_sRect	= irect( ivec2(0), WINDOW()->GetRenderParams().sWinSize );
		_sCamera.Resize( OSAPI()->GetRealViewportSize( Size() ), _fDist );

		SMAN()->Viewport( _sRect );
	}

/*
=================================================
	
=================================================
*/
	void CInputCameraComp::SetType(ECameraType eType)
	{
		switch ( eType )
		{
			case CT_FPS		 :	_pUpdate = & CInputCameraComp::_UpdateFPS;		break;
			case CT_FREE_FPS :	_pUpdate = & CInputCameraComp::_UpdateFreeFPS;	break;
			case CT_FLIGHT	 :	_pUpdate = & CInputCameraComp::_UpdateFlight;	break;
		};
	}

/*
=================================================
	
=================================================
*/
	void CInputCameraComp::SetSens(float fMouseSens, const vec3 &vAxisSens, float fVelocity,
									e_vec_swizzle::type eRotSw)
	{
		_vAxisSens		= vAxisSens;
		_fVelocity		= fVelocity * 1000.f;
		_fMouseSens		= fMouseSens;
		_eRotSwizzle	= eRotSw;
	}
	
/*
=================================================
	
=================================================
*/
	void CInputCameraComp::SetKeyBinds(const key_bind_array_t &aBinds)
	{
		for (usize i = 0; i < aBinds.Count(); ++i)
		{
			for (usize j = 0; j < _aActions.Count(); ++j)
			{
				if ( aBinds[i].eBind == _aActions[j].sBind.eBind )
				{
					_aActions[j].sBind = aBinds[i];
					break;
				}
			}
		}
	}
	
/*
=================================================
	
=================================================
*/
	bool CInputCameraComp::ProcessMessage(TSNCompMessage &sMsg)
	{
		typedef TSNCompMessage	e_msg;

		if ( sMsg.eType == e_msg::UPDATE )
		{
			_Update( sMsg.sData.sUpd.fTimeDelta );
		}

		return false;
	}
	
/*
=================================================
	
=================================================
*/
	void CInputCameraComp::_Update(float fTime)
	{
		_vTempMove = vec3(0.f);
		_vTempRot  = vec3( INPUT()->GetMouseDelta() * _fMouseSens );

		for (usize i = 0; i < _aActions.Count(); ++i)
		{
			if ( _aActions[i].sBind.CheckState<false>() )
				(this->*_aActions[i].pProc)( _aActions[i].iParam );
		}

		_vTempMove *= _fVelocity * fTime;
		_vTempRot  *= _vAxisSens / _sCamera.GetZoom();

		_vTempRot	= _vTempRot.Swizzle( _eRotSwizzle );

		(this->*_pUpdate)();
	}
	
/*
=================================================
	
=================================================
*/
	void CInputCameraComp::_UpdateFPS()
	{
		_sCamera.RotateFPS( _vTempRot.xy() );
		_sCamera.MoveFPS( _vTempMove );
	}

/*
=================================================
	
=================================================
*/
	void CInputCameraComp::_UpdateFreeFPS()
	{
		_sCamera.RotateFPS( _vTempRot.xy() );
		_sCamera.MoveFPSFree( _vTempMove );
	}

/*
=================================================
	
=================================================
*/
	void CInputCameraComp::_UpdateFlight()
	{
		_sCamera.RotateFlight( _vTempRot );
		_sCamera.MoveFlight( _vTempMove );
	}

/*
=================================================
	
=================================================
*/
	void CInputCameraComp::_MoveForward(int8 i)
	{
		_vTempMove += vec3( i, 0, 0 );
	}
	
/*
=================================================
	
=================================================
*/
	void CInputCameraComp::_MoveLeft(int8 i)
	{
		_vTempMove += vec3( 0, i, 0 );
	}
	
/*
=================================================
	
=================================================
*/
	void CInputCameraComp::_MoveUp(int8 i)
	{
		_vTempMove += vec3( 0, 0, i );
	}
	
/*
=================================================
	
=================================================
*/
	void CInputCameraComp::_Increase(int8 i)
	{
		_sCamera.Increase( _sCamera.GetZoom() + i * 0.02f );
	}
	
/*
=================================================
	
=================================================
*/
	void CInputCameraComp::_Roll(int8 i)
	{
		_vTempRot += vec3( 0, 0, i );
	}
	
/*
=================================================
	
=================================================
*/
	void CInputCameraComp::_Yaw(int8 i)
	{
		_vTempRot += vec3( 0, i, 0 );
	}
	
/*
=================================================
	
=================================================
*/
	void CInputCameraComp::_Pitch(int8 i)
	{
		_vTempRot += vec3( i, 0, 0 );
	}

//-------------------------------------------------------------------

}	// Scene
}	// Engine